home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / fax / src / faxd / FaxModem.c++ < prev    next >
C/C++ Source or Header  |  1994-08-01  |  38KB  |  1,549 lines

  1. /*    $Header: /usr/people/sam/fax/faxd/RCS/FaxModem.c++,v 1.95 1994/04/08 19:43:29 sam Rel $ */
  2. /*
  3.  * Copyright (c) 1990, 1991, 1992, 1993, 1994 Sam Leffler
  4.  * Copyright (c) 1991, 1992, 1993, 1994 Silicon Graphics, Inc.
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software and 
  7.  * its documentation for any purpose is hereby granted without fee, provided
  8.  * that (i) the above copyright notices and this permission notice appear in
  9.  * all copies of the software and related documentation, and (ii) the names of
  10.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  11.  * publicity relating to the software without the specific, prior written
  12.  * permission of Sam Leffler and Silicon Graphics.
  13.  * 
  14.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  15.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  16.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  17.  * 
  18.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  19.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  20.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  21.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  22.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  23.  * OF THIS SOFTWARE.
  24.  */
  25. #include <ctype.h>
  26. #include <stdlib.h>
  27.  
  28. #include "Everex.h"
  29. #include "Class1.h"
  30. #include "Class2Ersatz.h"
  31. #include "Class20.h"
  32. #include "FaxServer.h"
  33. #include "t.30.h"
  34.  
  35. /*
  36.  * Call status description strings.
  37.  */
  38. const char* FaxModem::callStatus[7] = {
  39.     "Call successful",                // OK
  40.     "Busy signal detected",            // BUSY
  41.     "No carrier detected",            // NOCARRIER
  42.     "No answer from remote",            // NOANSWER
  43.     "No local dialtone",            // NODIALTONE
  44.     "Invalid dialing command",            // ERROR
  45.     "Unknown problem (check modem power)",    // FAILURE
  46. };
  47. /*
  48.  * Service class descriptions.  The first three
  49.  * correspond to the EIA/TIA definitions.  The
  50.  * voice class is for ZyXEL modems.
  51.  */
  52. const char* FaxModem::serviceNames[9] = {
  53.     "\"Data\"",            // SERVICE_DATA
  54.     "\"Class 1\"",        // SERVICE_CLASS1
  55.     "\"Class 2\"",        // SERVICE_CLASS2
  56.     "\"Class 2.0\"",        // SERVICE_CLASS20 (XXX 3)
  57.     "",                // 4
  58.     "",                // 5
  59.     "",                // 6
  60.     "",                // 7
  61.     "\"Voice\"",        // SERVICE_VOICE
  62. };
  63. const char* FaxModem::ATresponses[13] = {
  64.     "Nothing",            // AT_NOTHING
  65.     "OK",            // AT_OK
  66.     "Connection established",    // AT_CONNECT
  67.     "No answer or ring back",    // AT_NOANSWER
  68.     "No carrier",        // AT_NOCARRIER
  69.     "No dial tone",        // AT_NODIALTONE
  70.     "Busy",            // AT_BUSY
  71.     "Phone off-hook",        // AT_OFFHOOK
  72.     "Ring",            // AT_RING
  73.     "Command error",        // AT_ERROR
  74.     "<Empty line>",        // AT_EMPTYLINE
  75.     "<Timeout>",        // AT_TIMEOUT
  76.     "<Unknown response>"    // AT_OTHER
  77. };
  78.  
  79. const u_char FaxModem::digitMap[12*2+1] = {
  80.     0x04, ' ', 0x0C, '0', 0x8C, '1', 0x4C, '2',
  81.     0xCC, '3', 0x2C, '4', 0xAC, '5', 0x6C, '6',
  82.     0xEC, '7', 0x1C, '8', 0x9C, '9', 0xD4, '+',
  83.     '\0'
  84. };
  85.  
  86. FaxModem::FaxModem(FaxServer& s, const ModemConfig& c)
  87.     : server(s)
  88.     , conf(c)
  89.     , mfrQueryCmd(c.mfrQueryCmd)
  90.     , modelQueryCmd(c.modelQueryCmd)
  91.     , revQueryCmd(c.revQueryCmd)
  92. {
  93.     /*
  94.      * The modem drivers and main server code require:
  95.      *
  96.      * echoOff        command echo disabled
  97.      * verboseResults    verbose command result strings
  98.      * resultCodes    result codes enabled
  99.      * onHook        modem initially on hook (hung up)
  100.      * noAutoAnswer    no auto-answer (we do it manually)
  101.      *
  102.      * In addition the following configuration is included
  103.      * in the reset command set:
  104.      *
  105.      * flowControl    DCE-DTE flow control method
  106.      * setupDTR        DTR management technique
  107.      * setupDCD        DCD management technique
  108.      * pauseTime    time to pause for "," when dialing
  109.      * waitTime        time to wait for carrier when dialing
  110.      *
  111.      * Any other modem-specific configuration that needs to
  112.      * be done at reset time should be implemented by overriding
  113.      * the FaxModem::reset method.
  114.      */
  115.     resetCmds = conf.resetCmds        // prepend to insure our needs
  116.           | conf.echoOffCmd
  117.           | conf.noAutoAnswerCmd
  118.           | conf.verboseResultsCmd
  119.           | conf.resultCodesCmd
  120.           | conf.onHookCmd
  121.           | conf.flowControlCmd
  122.           | conf.setupDTRCmd
  123.           | conf.setupDCDCmd
  124.           | conf.pauseTimeCmd
  125.           | conf.waitTimeCmd
  126.           ;
  127.     modemServices = 0;
  128.     rate = BR0;
  129.     flowControl = conf.flowControl;
  130.     iFlow = FLOW_CURRENT;
  131.     oFlow = FLOW_CURRENT;
  132. }
  133.  
  134. FaxModem::~FaxModem()
  135. {
  136. }
  137.  
  138. /*
  139.  * Deduce the type of modem supplied to the server
  140.  * and return an instance of the appropriate modem
  141.  * driver class.
  142.  */
  143. FaxModem*
  144. FaxModem::deduceModem(FaxServer& s, const ModemConfig& conf)
  145. {
  146.     fxStr h(conf.type);
  147.     h.raisecase();
  148.     if (h != "CLASS2.0" && h != "CLASS2" && h != "CLASS1" &&
  149.       h != "EVEREX" && h != "ABATON") {
  150.     if (h != "" && h != "UNKNOWN")
  151.         s.traceStatus(FAXTRACE_SERVER, "MODEM: Unknown type \"%s\" ignored",
  152.         (char*) conf.type);
  153.     h = "UNKNOWN";
  154.     }
  155.     /*
  156.      * Probe for modem using type, if specified; otherwise
  157.      * try Class 2, Class 1, and then old Everex types.
  158.      */
  159.     FaxModem* modem;
  160.     fxBool nothingTried = TRUE;
  161.     if (h == "CLASS2.0" || h == "UNKNOWN") {
  162.     nothingTried = FALSE;
  163.     modem = new Class20Modem(s, conf);
  164.     if (modem) {
  165.         if (modem->setupModem())
  166.         return modem;
  167.         delete modem;
  168.     }
  169.     }
  170.     if (h == "CLASS2" || h == "UNKNOWN") {
  171.     nothingTried = FALSE;
  172.     modem = new Class2ErsatzModem(s, conf);
  173.     if (modem) {
  174.         if (modem->setupModem())
  175.         return modem;
  176.         delete modem;
  177.     }
  178.     }
  179.     if (h == "CLASS1" || h == "UNKNOWN") {
  180.     nothingTried = FALSE;
  181.     modem = new Class1Modem(s, conf);
  182.     if (modem) {
  183.         if (modem->setupModem())
  184.         return modem;
  185.         delete modem;
  186.     }
  187.     }
  188.     if (h == "EVEREX" || h == "ABATON" || h == "UNKNOWN") {
  189.     nothingTried = FALSE;
  190.     modem = new EverexModem(s, conf);
  191.     if (modem) {
  192.         if (modem->setupModem())
  193.         return modem;
  194.         delete modem;
  195.     }
  196.     }
  197.     return (NULL);
  198. }
  199.  
  200. /*
  201.  * Default methods for modem driver interface.
  202.  */
  203. fxBool
  204. FaxModem::dataService()
  205. {
  206.     return (FALSE);
  207. }
  208.  
  209. fxBool
  210. FaxModem::voiceService()
  211. {
  212.     return (FALSE);
  213. }
  214.  
  215. CallStatus
  216. FaxModem::dial(const char* number)
  217. {
  218.     protoTrace("DIAL %s", number);
  219.     char buf[256];
  220.     sprintf(buf, (const char*) conf.dialCmd, number);
  221.     return (atCmd(buf, AT_NOTHING) ? dialResponse() : FAILURE);
  222. }
  223.  
  224. /*
  225.  * Set of status codes we expect to receive
  226.  * from a modem in response to an A (answer
  227.  * the phone) command.
  228.  */
  229. static const AnswerMsg answerMsgs[] = {
  230. { "CONNECT FAX",11,
  231.    FaxModem::AT_NOTHING,    FaxModem::OK,         FaxModem::CALLTYPE_FAX },
  232. { "CONNECT",     7,
  233.    FaxModem::AT_NOTHING,    FaxModem::OK,         FaxModem::CALLTYPE_DATA },
  234. { "NO ANSWER",   9,
  235.    FaxModem::AT_NOTHING,    FaxModem::NOANSWER,  FaxModem::CALLTYPE_ERROR },
  236. { "NO CARRIER", 10,
  237.    FaxModem::AT_NOTHING,    FaxModem::NOCARRIER, FaxModem::CALLTYPE_ERROR },
  238. { "NO DIALTONE",11,
  239.    FaxModem::AT_NOTHING,    FaxModem::NODIALTONE,FaxModem::CALLTYPE_ERROR },
  240. { "ERROR",       5,
  241.    FaxModem::AT_NOTHING,    FaxModem::ERROR,     FaxModem::CALLTYPE_ERROR },
  242. { "FAX",     3,
  243.    FaxModem::AT_CONNECT,    FaxModem::OK,         FaxModem::CALLTYPE_FAX },
  244. { "DATA",     4,
  245.    FaxModem::AT_CONNECT,    FaxModem::OK,         FaxModem::CALLTYPE_DATA },
  246. };
  247. #define    NANSWERS    (sizeof (answerMsgs) / sizeof (answerMsgs[0]))
  248.  
  249. const AnswerMsg*
  250. FaxModem::findAnswer(const char* s)
  251. {
  252.     for (u_int i = 0; i < NANSWERS; i++)
  253.     if (strneq(s, answerMsgs[i].msg, answerMsgs[i].len))
  254.         return (&answerMsgs[i]);
  255.     return (NULL);
  256. }
  257.  
  258. /*
  259.  * Deduce connection kind: fax, data, or voice.
  260.  */
  261. CallType
  262. FaxModem::answerResponse(fxStr& emsg)
  263. {
  264.     CallStatus cs = FAILURE;
  265.     ATResponse r;
  266.     do {
  267.     r = atResponse(rbuf, conf.answerResponseTimeout);
  268. again:
  269.     if (r == AT_TIMEOUT) {
  270.         emsg = "Ring detected without successful handshake"; 
  271.         return (CALLTYPE_ERROR);
  272.     }
  273.     const AnswerMsg* am = findAnswer(rbuf);
  274.     if (am != NULL) {
  275.         if (am->expect != AT_NOTHING && conf.waitForConnect) {
  276.         /*
  277.          * Response string is an intermediate result that
  278.          * is only meaningful if followed by AT response
  279.          * am->next.  Read the next response from the modem
  280.          * and if it's the expected one, use the message
  281.          * to intuit the call type.  Otherwise, discard
  282.          * the intermediate response string and process the
  283.          * call according to the newly read response.
  284.          * This is intended to deal with modems that send
  285.          *   <something>
  286.          *   CONNECT
  287.          * (such as the Boca 14.4).
  288.          */
  289.         r = atResponse(rbuf, conf.answerResponseTimeout);
  290.         if (r != am->expect)
  291.             goto again;
  292.         }
  293.         if (am->status == OK)        // success
  294.         return (am->type);
  295.         cs = am->status;
  296.         break;
  297.     }
  298.     } while (r != AT_EMPTYLINE);
  299.     emsg = callStatus[cs];
  300.     return (CALLTYPE_ERROR);
  301. }
  302.  
  303. CallType
  304. FaxModem::answerCall(AnswerType atype, fxStr& emsg)
  305. {
  306.     CallType ctype = CALLTYPE_ERROR;
  307.     /*
  308.      * If the request has no type-specific commands
  309.      * to use, then just use the normal commands
  310.      * intended for answering any type of call.
  311.      */
  312.     fxStr answerCmd;
  313.     switch (atype) {
  314.     case ANSTYPE_FAX:    answerCmd = conf.answerFaxCmd; break;
  315.     case ANSTYPE_DATA:    answerCmd = conf.answerDataCmd; break;
  316.     case ANSTYPE_VOICE:    answerCmd = conf.answerVoiceCmd; break;
  317.     }
  318.     if (answerCmd == "")
  319.     answerCmd = conf.answerAnyCmd;
  320.     if (atCmd(answerCmd, AT_NOTHING)) {
  321.     ctype = answerResponse(emsg);
  322.     if (ctype == CALLTYPE_UNKNOWN) {
  323.         /*
  324.          * The response does not uniquely identify the type
  325.          * of call; assume the type corresponds to the type
  326.          * of the answer request.
  327.          */
  328.         static CallType unknownCall[] = {
  329.         CALLTYPE_FAX,    // ANSTYPE_ANY (default)
  330.         CALLTYPE_DATA,    // ANSTYPE_DATA
  331.         CALLTYPE_FAX,    // ANSTYPE_FAX
  332.         CALLTYPE_VOICE,    // ANSTYPE_VOICE
  333.         };
  334.         ctype = unknownCall[atype];
  335.     }
  336.     /*
  337.      * Send any configured commands to the modem once the
  338.      * type of the call has been established.  These commands
  339.      * normally configure flow control and buad rate for
  340.      * modems that, for example, require a fixed baud rate
  341.      * and flow control scheme when receiving fax.
  342.      */ 
  343.     fxStr beginCmd;
  344.     switch (ctype) {
  345.     case CALLTYPE_FAX:    beginCmd = conf.answerFaxBeginCmd; break;
  346.     case CALLTYPE_DATA:    beginCmd = conf.answerDataBeginCmd; break;
  347.     case CALLTYPE_VOICE:    beginCmd = conf.answerVoiceBeginCmd; break;
  348.     }
  349.     if (beginCmd != "")
  350.         (void) atCmd(beginCmd);
  351.     }
  352.     return (ctype);
  353. }
  354.  
  355. void FaxModem::sendBegin()    {}
  356. void FaxModem::sendSetupPhaseB(){}
  357. void FaxModem::sendEnd()    {}
  358.  
  359. /*
  360.  * Modem capability (and related) query interfaces.
  361.  */
  362.  
  363. static u_int
  364. bestBit(u_int bits, u_int top, u_int bot)
  365. {
  366.     while (top > bot && (bits & BIT(top)) == 0)
  367.     top--;
  368.     return (top);
  369. }
  370.  
  371. /*
  372.  * Return Class 2 code for best modem signalling rate.
  373.  */
  374. u_int
  375. FaxModem::getBestSignallingRate() const
  376. {
  377.     return bestBit(modemParams.br, BR_14400, BR_2400);
  378. }
  379.  
  380. /*
  381.  * Compare the requested signalling rate against
  382.  * those the modem can do and return the appropriate
  383.  * Class 2 bit rate code.
  384.  */
  385. int
  386. FaxModem::selectSignallingRate(int br) const
  387. {
  388.     for (; br >= 0 && (modemParams.br & BIT(br)) == 0; br--)
  389.     ;
  390.     return (br);
  391. }
  392.  
  393. /*
  394.  * Set data transfer timeout and adjust according
  395.  * to the negotiated bit rate.
  396.  */
  397. void
  398. FaxModem::setDataTimeout(long secs, u_int br)
  399. {
  400.     dataTimeout = secs*1000;    // 9600 baud timeout/data write (ms)
  401.     switch (br) {
  402.     case BR_2400:    dataTimeout *= 4; break;
  403.     case BR_4800:    dataTimeout *= 2; break;
  404.     case BR_9600:    dataTimeout = (4*dataTimeout)/3; break;
  405.     // could shrink timeout for br > 9600
  406.     }
  407. }
  408.  
  409. /*
  410.  * Compare the requested min scanline time
  411.  * to what the modem can do and return the
  412.  * lowest time the modem can do.
  413.  */
  414. int
  415. FaxModem::selectScanlineTime(int st) const
  416. {
  417.     for (; st < ST_40MS && (modemParams.st & BIT(st)) == 0; st++)
  418.     ;
  419.     return (st);
  420. }
  421.  
  422. /*
  423.  * Return the best min scanline time the modem
  424.  * is capable of supporting.
  425.  */
  426. u_int
  427. FaxModem::getBestScanlineTime() const
  428. {
  429.     u_int st;
  430.     for (st = ST_0MS; st < ST_40MS; st++)
  431.     if (modemParams.st & BIT(st))
  432.         break;
  433.     return st;
  434. }
  435.  
  436. /*
  437.  * Return the best vres the modem supports.
  438.  */
  439. u_int
  440. FaxModem::getBestVRes() const
  441. {
  442.     return bestBit(modemParams.vr, VR_FINE, VR_NORMAL);
  443. }
  444.  
  445. /*
  446.  * Return the best page width the modem supports.
  447.  */
  448. u_int
  449. FaxModem::getBestPageWidth() const
  450. {
  451.     // XXX NB: we don't use anything > WD_2432
  452.     return bestBit(modemParams.wd, WD_2432, WD_1728);
  453. }
  454.  
  455. /*
  456.  * Return the best page length the modem supports.
  457.  */
  458. u_int
  459. FaxModem::getBestPageLength() const
  460. {
  461.     return bestBit(modemParams.ln, LN_INF, LN_A4);
  462. }
  463.  
  464. /*
  465.  * Return the best data format the modem supports.
  466.  */
  467. u_int
  468. FaxModem::getBestDataFormat() const
  469. {
  470.     return bestBit(modemParams.df, DF_2DMMR, DF_1DMR);
  471. }
  472.  
  473. /*
  474.  * Return whether or not the modem supports 2DMR.
  475.  */
  476. fxBool
  477. FaxModem::supports2D() const
  478. {
  479.     return modemParams.df & BIT(DF_2DMR);
  480. }
  481.  
  482. /*
  483.  * Return whether or not received EOLs are byte aligned.
  484.  */
  485. fxBool
  486. FaxModem::supportsEOLPadding() const
  487. {
  488.     return FALSE;
  489. }
  490.  
  491. /*
  492.  * Return whether or not the modem supports the
  493.  * specified vertical resolution.  Note that we're
  494.  * rather tolerant because of potential precision
  495.  * problems and general sloppiness on the part of
  496.  * applications writing TIFF files.
  497.  */
  498. fxBool
  499. FaxModem::supportsVRes(float res) const
  500. {
  501.     if (75 <= res && res < 120)
  502.     return modemParams.vr & BIT(VR_NORMAL);
  503.     else if (150 <= res && res < 250)
  504.     return modemParams.vr & BIT(VR_FINE);
  505.     else
  506.     return FALSE;
  507. }
  508.  
  509. /*
  510.  * Return whether or not the modem supports the
  511.  * specified page width.
  512.  */
  513. fxBool
  514. FaxModem::supportsPageWidth(u_int w) const
  515. {
  516.     switch (w) {
  517.     case 1728:    return modemParams.wd & BIT(WD_1728);
  518.     case 2048:    return modemParams.wd & BIT(WD_2048);
  519.     case 2432:    return modemParams.wd & BIT(WD_2432);
  520.     case 1216:    return modemParams.wd & BIT(WD_1216);
  521.     case 864:    return modemParams.wd & BIT(WD_864);
  522.     }
  523.     return FALSE;
  524. }
  525.  
  526. /*
  527.  * Return whether or not the modem supports the
  528.  * specified page length.  As above for vertical
  529.  * resolution we're lenient in what we accept.
  530.  */
  531. fxBool
  532. FaxModem::supportsPageLength(u_int l) const
  533. {
  534.     // XXX probably need to be more forgiving with values
  535.     if (270 < l && l <= 330)
  536.     return modemParams.ln & (BIT(LN_A4)|BIT(LN_INF));
  537.     else if (330 < l && l <= 390)
  538.     return modemParams.ln & (BIT(LN_B4)|BIT(LN_INF));
  539.     else
  540.     return modemParams.ln & BIT(LN_INF);
  541. }
  542.  
  543. /*
  544.  * Return modems best capabilities for setting up
  545.  * the initial T.30 DIS when receiving data.
  546.  */
  547. u_int
  548. FaxModem::modemDIS() const
  549. {
  550.     u_int DIS = DIS_T4RCVR
  551.           | Class2Params::vrDISTab[getBestVRes()]
  552.           | Class2Params::brDISTab[getBestSignallingRate()]
  553.           | Class2Params::wdDISTab[getBestPageWidth()]
  554.           | Class2Params::lnDISTab[getBestPageWidth()]
  555.           | Class2Params::dfDISTab[getBestDataFormat()]
  556.           | Class2Params::stDISTab[getBestScanlineTime()]
  557.           ;
  558.     // tack on one extension byte
  559.     DIS = (DIS | DIS_XTNDFIELD) << 8;
  560.     if (modemParams.df >= DF_2DMRUNCOMP)
  561.     DIS |= DIS_2DUNCOMP;
  562.     if (modemParams.df >= DF_2DMMR)
  563.     DIS |= DIS_G4COMP;
  564.     return (DIS);
  565. }
  566.  
  567. /*
  568.  * Tracing support.
  569.  */
  570.  
  571. /*
  572.  * Trace a MODEM-communication-related activity.
  573.  */
  574. void
  575. FaxModem::modemTrace(const char* va_alist ...)
  576. #define    fmt va_alist
  577. {
  578.     va_list ap;
  579.     va_start(ap, fmt);
  580.     char buf[1024];
  581.     strcpy(buf, "MODEM ");
  582.     strcat(buf, fmt);
  583.     server.vtraceStatus(FAXTRACE_MODEMCOM, buf, ap);
  584.     va_end(ap);
  585. }
  586. #undef fmt
  587.  
  588. /*
  589.  * Trace a modem capability.
  590.  */
  591. void
  592. FaxModem::modemCapability(const char* va_alist ...)
  593. #define    fmt va_alist
  594. {
  595.     va_list ap;
  596.     va_start(ap, fmt);
  597.     char buf[1024];
  598.     strcpy(buf, "MODEM ");
  599.     strcat(buf, fmt);
  600.     server.vtraceStatus(FAXTRACE_MODEMCAP, buf, ap);
  601.     va_end(ap);
  602. }
  603. #undef fmt
  604.  
  605. /*
  606.  * Indicate a modem supports some capability.
  607.  */
  608. void
  609. FaxModem::modemSupports(const char* va_alist ...)
  610. #define    fmt va_alist
  611. {
  612.     va_list ap;
  613.     va_start(ap, fmt);
  614.     char buf[1024];
  615.     strcpy(buf, "MODEM Supports ");
  616.     strcat(buf, fmt);
  617.     server.vtraceStatus(FAXTRACE_MODEMCAP, buf, ap);
  618.     va_end(ap);
  619. }
  620. #undef fmt
  621.  
  622. /*
  623.  * Trace a protocol-related activity.
  624.  */
  625. void
  626. FaxModem::protoTrace(const char* va_alist ...)
  627. #define    fmt va_alist
  628. {
  629.     va_list ap;
  630.     va_start(ap, fmt);
  631.     server.vtraceStatus(FAXTRACE_PROTOCOL, fmt, ap);
  632.     va_end(ap);
  633. }
  634. #undef fmt
  635.  
  636. /*
  637.  * Trace a server-level activity.
  638.  */
  639. void
  640. FaxModem::serverTrace(const char* va_alist ...)
  641. #define    fmt va_alist
  642. {
  643.     va_list ap;
  644.     va_start(ap, fmt);
  645.     server.vtraceStatus(FAXTRACE_SERVER, fmt, ap);
  646.     va_end(ap);
  647. }
  648. #undef fmt
  649.  
  650. /*
  651.  * Trace a modem capability bit mask.
  652.  */
  653. void
  654. FaxModem::traceBits(u_int bits, const char* bitNames[])
  655. {
  656.     for (u_int i = 0; bits; i++)
  657.     if (BIT(i) & bits) {
  658.         modemSupports(bitNames[i]);
  659.         bits &= ~BIT(i);
  660.     }
  661. }
  662.  
  663. /*
  664.  * Trace a modem's capabilities.
  665.  */
  666. void
  667. FaxModem::traceModemParams()
  668. {
  669.     traceBits(modemParams.vr, Class2Params::vresNames);
  670.     traceBits(modemParams.br, Class2Params::bitRateNames);
  671.     traceBits(modemParams.wd, Class2Params::pageWidthNames);
  672.     traceBits(modemParams.ln, Class2Params::pageLengthNames);
  673.     traceBits(modemParams.df, Class2Params::dataFormatNames);
  674.     if (modemParams.ec & (BIT(EC_ENABLE)))
  675.     modemSupports("error correction");
  676.     if (modemParams.bf & BIT(BF_ENABLE))
  677.     modemSupports("binary file transfer");
  678.     traceBits(modemParams.st, Class2Params::scanlineTimeNames);
  679. }
  680.  
  681. void
  682. FaxModem::tracePPM(const char* dir, u_int ppm)
  683. {
  684.     static const char* ppmNames[16] = {
  685.     "unknown PPM 0x00",
  686.     "EOM (more documents)",                // FCF_EOM
  687.     "MPS (more pages, same document)",        // FCF_MPS
  688.     "unknown PPM 0x03",
  689.     "EOP (no more pages or documents)",        // FCF_EOP
  690.     "unknown PPM 0x05",
  691.     "unknown PPM 0x06",
  692.     "unknown PPM 0x07",
  693.     "unknown PPM 0x08",
  694.     "PRI-EOM (more documents after interrupt)",    // FCF_PRI_EOM
  695.     "PRI-MPS (more pages after interrupt)",        // FCF_PRI_MPS
  696.     "unknown PPM 0x0B",
  697.     "PRI-EOP (no more pages after interrupt)",    // FCF_PRI_EOP
  698.     "unknown PPM 0x0D",
  699.     "unknown PPM 0x0E",
  700.     };
  701.     protoTrace("%s %s", dir, ppmNames[ppm&0xf]);
  702. }
  703.  
  704. void
  705. FaxModem::tracePPR(const char* dir, u_int ppr)
  706. {
  707.     static const char* pprNames[16] = {
  708.     "unknown PPR 0x00",
  709.     "MCF (message confirmation)",            // FCF_MCF/PPR_MCF
  710.     "RTN (retrain negative)",            // FCF_RTN/PPR_RTN
  711.     "RTP (retrain positive)",            // FCF_RTP/PPR_RTP
  712.     "PIN (procedural interrupt negative)",        // FCF_PIN/PPR_PIN
  713.     "PIP (procedural interrupt positive)",        // FCF_PIP/PPR_PIP
  714.     "unknown PPR 0x06",
  715.     "unknown PPR 0x07",
  716.     "CRP (command repeat)",                // FCF_CRP
  717.     "unknown PPR 0x09",
  718.     "unknown PPR 0x0A",
  719.     "unknown PPR 0x0B",
  720.     "unknown PPR 0x0C",
  721.     "unknown PPR 0x0D",
  722.     "unknown PPR 0x0E",
  723.     "DCN (disconnect)",                // FCF_DCN
  724.     };
  725.     protoTrace("%s %s", dir, pprNames[ppr&0xf]);
  726. }
  727.  
  728. #define    EOLcheck(w,mask,code) \
  729.     if ((w & mask) == code) { w |= mask; return (TRUE); }
  730.  
  731. /*
  732.  * Check the last 24 bits of received T.4-encoded
  733.  * data (presumed to be in LSB2MSB bit order) for
  734.  * an EOL code and, if one is found, foul the data
  735.  * to insure future calls do not re-recognize an
  736.  * EOL code.
  737.  */
  738. fxBool
  739. FaxModem::EOLcode(u_long& w)
  740. {
  741.     if ((w & 0x00f00f) == 0) {
  742.     EOLcheck(w, 0x00f0ff, 0x000080);
  743.     EOLcheck(w, 0x00f87f, 0x000040);
  744.     EOLcheck(w, 0x00fc3f, 0x000020);
  745.     EOLcheck(w, 0x00fe1f, 0x000010);
  746.     }
  747.     if ((w & 0x00ff00) == 0) {
  748.     EOLcheck(w, 0x00ff0f, 0x000008);
  749.     EOLcheck(w, 0x80ff07, 0x000004);
  750.     EOLcheck(w, 0xc0ff03, 0x000002);
  751.     EOLcheck(w, 0xe0ff01, 0x000001);
  752.     }
  753.     if ((w & 0xf00f00) == 0) {
  754.     EOLcheck(w, 0xf0ff00, 0x008000);
  755.     EOLcheck(w, 0xf87f00, 0x004000);
  756.     EOLcheck(w, 0xfc3f00, 0x002000);
  757.     EOLcheck(w, 0xfe1f00, 0x001000);
  758.     }
  759.     return (FALSE);
  760. }
  761. #undef EOLcheck
  762.  
  763. void
  764. FaxModem::startPageRecv()
  765. {
  766.     recvEOLCount = 0;
  767.     recvWord = 0xffffff;
  768.     memset(RTCbuf, 0, sizeof (RTCbuf));
  769. }
  770.  
  771. void
  772. FaxModem::recvPageData(TIFF* tif, u_char* bp, int n)
  773. {
  774.     TIFFWriteRawStrip(tif, 0, bp, n);
  775.     /*
  776.      * Capture the last 40 bytes of data so that
  777.      * we can verify the RTC at the end when adjusting
  778.      * the received line count.  10 bytes is all
  779.      * that we need; it is enough to hold a 2D RTC.
  780.      */
  781.     int cc = n - 40;
  782.     if (cc > 0)
  783.     memcpy(RTCbuf, bp+cc, 40);
  784.     else
  785.     memcpy(RTCbuf-cc, bp, n);
  786.     /*
  787.      * Many modems (ZyXEL, Supra) do not return valid line
  788.      * counts, so count them here.  Note that this code
  789.      * assumes data is in LSB2MSB order.
  790.      */
  791.     u_long rows = 0;
  792.     u_long w = recvWord;
  793.     for (cc = n; cc > 0; cc--) {
  794.     w = (w<<8) | *bp++;
  795.     if (EOLcode(w))
  796.         rows++;
  797.     }
  798.     recvWord = w;
  799.     recvEOLCount += rows;
  800.     protoTrace("RECV: %d bytes of data, %lu lines", n, rows);
  801. }
  802.  
  803. static const int EOL = 0x001;        // end-of-line code (11 0's + 1)
  804.  
  805. #define    BITCASE(b)            \
  806.     case b:                \
  807.     code <<= 1;            \
  808.     if (data & b) code |= 1;    \
  809.     len++;                \
  810.     if (code > 0) { bit = (b<<1); break; }
  811.  
  812. /*
  813.  * Parse RTC and adjust line count.
  814.  */
  815. void
  816. FaxModem::endPageRecv(const Class2Params& params)
  817. {
  818.     fxBool startOfRTC = TRUE;
  819.     /*
  820.      * Locate the start of the RTC by looking for
  821.      * two consecutive EOL codes in the RTC buffer.
  822.      * From that point onward, we deduct any EOL codes
  823.      * that we see from the count of received lines.
  824.      */
  825.     u_int i = 0;
  826.     int bit = 0x01;
  827.     int data = 0;
  828.     fxBool emptyLine = FALSE;
  829.     for (;;) {
  830.     short code = 0;
  831.     short len = 0;
  832.     switch (bit & 0xff) {
  833.     again:
  834.     BITCASE(0x01);
  835.     BITCASE(0x02);
  836.     BITCASE(0x04);
  837.     BITCASE(0x08);
  838.     BITCASE(0x10);
  839.     BITCASE(0x20);
  840.     BITCASE(0x40);
  841.     BITCASE(0x80);
  842.     default:
  843.         if (i == sizeof (RTCbuf))
  844.         return;
  845.         data = RTCbuf[i++];
  846.         goto again;
  847.     }
  848.     if (len >= 12 && code == EOL) {        // EOL code
  849.         if (params.is2D()) {        // snarf tag bit
  850.         if ((bit & 0xff) == 0) {
  851.             if (i == sizeof (RTCbuf))
  852.             return;
  853.             data = RTCbuf[i++];
  854.             bit = 0x01;
  855.         }
  856.         bit <<= 1;
  857.         }
  858.         if (emptyLine) {            // consecutive EOL codes
  859.         recvEOLCount -= 1+startOfRTC;
  860.         startOfRTC = FALSE;
  861.         } else
  862.         emptyLine = TRUE;
  863.     } else                    // something other than EOL
  864.         emptyLine = FALSE;
  865.     }
  866. }
  867.  
  868. u_long
  869. FaxModem::getRecvEOLCount() const
  870. {
  871.     return recvEOLCount;
  872. }
  873.  
  874. /*
  875.  * Modem i/o support.
  876.  */
  877.  
  878. int
  879. FaxModem::getModemLine(char buf[], u_int bufSize, long ms)
  880. {
  881.     int n = server.getModemLine(buf, bufSize, ms);
  882.     if (n > 0)
  883.     trimModemLine(buf, n);
  884.     return (n);
  885. }
  886. int FaxModem::getModemChar(long ms) { return server.getModemChar(ms); }
  887. int FaxModem::getModemDataChar()    { return server.getModemChar(dataTimeout); }
  888.  
  889. fxBool
  890. FaxModem::putModemDLEData(const u_char* data, u_int cc, const u_char* bitrev, long ms)
  891. {
  892.     u_char dlebuf[2*1024];
  893.     while (cc > 0) {
  894.     if (wasTimeout() || abortRequested())
  895.         return (FALSE);
  896.     /*
  897.      * Copy to temp buffer, doubling DLE's.
  898.      */
  899.     u_int i, j;
  900.     u_int n = fxmin(cc, conf.maxPacketSize);
  901.     n = fxmin(n, sizeof (dlebuf)/2);
  902.     for (i = 0, j = 0; i < n; i++, j++) {
  903.         dlebuf[j] = bitrev[data[i]];
  904.         if (dlebuf[j] == DLE)
  905.         dlebuf[++j] = DLE;
  906.     }
  907.     if (!putModem(dlebuf, j, ms))
  908.         return (FALSE);
  909.     data += n;
  910.     cc -= n;
  911.     if (cc > 0 && conf.interPacketDelay)
  912.         pause(conf.interPacketDelay);
  913.     }
  914.     return (TRUE);
  915. }
  916.  
  917. void FaxModem::flushModemInput()
  918.     { server.modemFlushInput(); }
  919. fxBool FaxModem::putModem(void* d, int n, long ms)
  920.     { return server.putModem(d, n, ms); }
  921. fxBool FaxModem::putModemData(void* d, int n)
  922.     { return server.putModem(d, n, dataTimeout); }
  923.  
  924. fxBool
  925. FaxModem::putModemLine(const char* cp)
  926. {
  927.     u_int cc = strlen(cp);
  928.     server.traceStatus(FAXTRACE_MODEMCOM, "<-- [%u:%s]", cc, cp);
  929.     while (cc > 0) {
  930.     u_int n = fxmin(cc, conf.maxPacketSize);
  931.     if (!server.putModem1(cp, n))
  932.         return (FALSE);
  933.     cp += n;
  934.     cc -= n;
  935.     if (conf.interPacketDelay)
  936.         pause(conf.interPacketDelay);
  937.     }
  938.     static const char CR = '\r';
  939.     server.putModem1(&CR, 1);
  940.     return (TRUE);
  941. }
  942.  
  943. void FaxModem::startTimeout(long ms) { server.startTimeout(ms); }
  944. void FaxModem::stopTimeout(const char* w){ server.stopTimeout(w); }
  945.  
  946. const u_int MSEC_PER_SEC = 1000;
  947.  
  948. #include <osfcn.h>
  949. #include <sys/time.h>
  950.  
  951. void
  952. FaxModem::pause(u_int ms)
  953. {
  954.     if (ms == 0)
  955.     return;
  956.     protoTrace("DELAY %u ms", ms);
  957.     struct timeval tv;
  958.     tv.tv_sec = ms / MSEC_PER_SEC;
  959.     tv.tv_usec = (ms % MSEC_PER_SEC) * 1000;
  960.     (void) select(0, 0, 0, 0, &tv);
  961. }
  962.  
  963. /*
  964.  * Reset the modem and set the DTE-DCE rate.
  965.  */
  966. fxBool
  967. FaxModem::selectBaudRate(BaudRate br, FlowControl i, FlowControl o)
  968. {
  969.     rate = br;
  970.     iFlow = i;
  971.     oFlow = o;
  972.     for (u_int n = 3; n != 0; n--)
  973.     if (reset(5*1000))
  974.         return (TRUE);
  975.     return (FALSE);
  976. }
  977.  
  978. fxBool FaxModem::sendBreak(fxBool pause)
  979.     { return server.sendBreak(pause); }
  980. fxBool
  981. FaxModem::setBaudRate(BaudRate r)
  982. {
  983.     if (server.setBaudRate(r)) {
  984.     if (conf.baudRateDelay)
  985.         pause(conf.baudRateDelay);
  986.     return (TRUE);
  987.     } else
  988.     return (FALSE);
  989. }
  990. fxBool
  991. FaxModem::setBaudRate(BaudRate r, FlowControl i, FlowControl o)
  992. {
  993.     if (server.setBaudRate(r,i,o)) {
  994.     if (conf.baudRateDelay)
  995.         pause(conf.baudRateDelay);
  996.     return (TRUE);
  997.     } else
  998.     return (FALSE);
  999. }
  1000. fxBool FaxModem::setXONXOFF(FlowControl i, FlowControl o, SetAction a)
  1001.     { return server.setXONXOFF(i,o,a); }
  1002. fxBool FaxModem::setDTR(fxBool onoff)
  1003.     { return server.setDTR(onoff); }
  1004. fxBool FaxModem::setInputBuffering(fxBool onoff)
  1005.     { return server.setInputBuffering(onoff); }
  1006. fxBool FaxModem::modemStopOutput()
  1007.     { return server.modemStopOutput(); }
  1008.  
  1009. /*
  1010.  * Miscellaneous server interfaces hooks.
  1011.  */
  1012.  
  1013. fxBool FaxModem::getProtocolTracing()
  1014.     { return (server.getSessionTracing() & FAXTRACE_PROTOCOL) != 0; }
  1015. fxBool FaxModem::getHDLCTracing()
  1016.     { return (server.getSessionTracing() & FAXTRACE_HDLC) != 0; }
  1017.  
  1018. fxBool FaxModem::sendSetupParams(TIFF* tif, Class2Params& params,
  1019.     FaxMachineInfo& info, fxStr& emsg)
  1020. {
  1021.     return server.sendSetupParams(tif, params, info, emsg);
  1022. }
  1023.  
  1024. fxBool
  1025. FaxModem::recvCheckTSI(const fxStr& tsi)
  1026. {
  1027.     fxStr s(tsi);
  1028.     s.remove(0, s.skip(0,' '));        // strip leading white space
  1029.     u_int pos = s.skipR(s.length(),' ');
  1030.     s.remove(pos, s.length() - pos);    // and trailing white space
  1031.     return server.recvCheckTSI(s);
  1032. }
  1033. void
  1034. FaxModem::recvCSI(fxStr& csi)
  1035. {
  1036.     csi.remove(0, csi.skip(0,' '));    // strip leading white space
  1037.     u_int pos = csi.skipR(csi.length(),' ');
  1038.     csi.remove(pos, csi.length() - pos);// and trailing white space
  1039.     protoTrace("REMOTE CSI \"%s\"", (char*) csi);
  1040. }
  1041. void FaxModem::recvDCS(Class2Params& params)
  1042.     { server.recvDCS(params); }
  1043. void FaxModem::recvNSF(u_int nsf)
  1044.     { server.recvNSF(nsf); }
  1045.  
  1046. void
  1047. FaxModem::recvSetupPage(TIFF* tif, long group3opts, int fillOrder)
  1048. {
  1049.     server.recvSetupPage(tif, group3opts, fillOrder);
  1050.     /*
  1051.      * Record the file offset to the start of the data
  1052.      * in the file.  We write zero bytes to force the
  1053.      * strip offset to be setup in case this is the first
  1054.      * time the strip is being written.
  1055.      */
  1056.     u_char null[1];
  1057.     (void) TIFFWriteRawStrip(tif, 0, null, 0);
  1058.     u_long* lp;
  1059.     (void) TIFFGetField(tif, TIFFTAG_STRIPOFFSETS, &lp);
  1060.     savedWriteOff = lp[0];
  1061. }
  1062.  
  1063. /*
  1064.  * Reset the TIFF state for the current page so that
  1065.  * subsequent data overwrites anything previously
  1066.  * written.  This is done by reseting the file offset
  1067.  * and setting the strip's bytecount and offset to
  1068.  * values they had at the start of the page.  This
  1069.  * scheme assumes that only page data is written to
  1070.  * the file between the time recvSetupPage is called
  1071.  * and recvResetPage is called.
  1072.  */
  1073. void
  1074. FaxModem::recvResetPage(TIFF* tif)
  1075. {
  1076.     u_long* lp;
  1077.     TIFFSetWriteOffset(tif, 0);        // force library to reset state
  1078.     TIFFGetField(tif, TIFFTAG_STRIPOFFSETS, &lp);    lp[0] = savedWriteOff;
  1079.     TIFFGetField(tif, TIFFTAG_STRIPBYTECOUNTS, &lp);    lp[0] = 0;
  1080. }
  1081.  
  1082. fxBool FaxModem::abortRequested()
  1083.     { return server.abortRequested(); }
  1084.  
  1085. void FaxModem::beginTimedTransfer()        { server.timeout = FALSE; }
  1086. void FaxModem::endTimedTransfer()        {}
  1087. fxBool FaxModem::wasTimeout()            { return server.timeout; }
  1088. void FaxModem::setTimeout(fxBool b)        { server.timeout = b; }
  1089.  
  1090. void FaxModem::countPage()            { server.npages++; }
  1091.  
  1092. /*
  1093.  * Parsing support routines.
  1094.  */
  1095.  
  1096. /*
  1097.  * Cleanup a response line from the modem.  This removes
  1098.  * leading white space and any prefixing "+F<mumble>=" crap
  1099.  * that some Class 2 modems put at the front, as well as
  1100.  * any trailing white space.
  1101.  */
  1102. void
  1103. FaxModem::trimModemLine(char buf[], int& cc)
  1104. {
  1105.     // trim trailing white space
  1106.     if (cc > 0 && isspace(buf[cc-1])) {
  1107.     do {
  1108.         cc--;
  1109.     } while (cc > 0 && isspace(buf[cc-1]));
  1110.     buf[cc] = '\0';
  1111.     }
  1112.     if (cc > 0) {
  1113.     u_int i = 0;
  1114.     // leading white space
  1115.     while (i < cc && isspace(buf[i]))
  1116.         i++;
  1117.     // check for a leading +F<mumble>=
  1118.     if (i+1 < cc && buf[i] == '+' && buf[i+1] == 'F') {
  1119.         u_int j = i;
  1120.         for (i += 2; i < cc && buf[i] != '='; i++)
  1121.          ;
  1122.         if (i < cc) {    // trim more white space
  1123.         for (i++; i < cc && isspace(buf[i]); i++)
  1124.             ;
  1125.         } else        // no '=', back out
  1126.         i = j;
  1127.     }
  1128.     cc -= i;
  1129.     memmove(buf, buf+i, cc+1);
  1130.     }
  1131. }
  1132.  
  1133. u_int
  1134. FaxModem::fromHex(const char* cp, int n)
  1135. {
  1136.     if (n == -1)
  1137.     n = strlen(cp);
  1138.     u_int v = 0;
  1139.     while (n-- > 0) {
  1140.     int c = *cp++;
  1141.     if (isxdigit(c)) {
  1142.         if (isdigit(c))
  1143.         c -= '0';
  1144.         else
  1145.         c = (c - 'A') + 10;
  1146.         v = (v << 4) + c;
  1147.     }
  1148.     }
  1149.     return (v);
  1150. }
  1151.  
  1152. fxStr
  1153. FaxModem::toHex(int v, int ndigits)
  1154. {
  1155.     char buf[9];
  1156.     assert(ndigits <= 8);
  1157.     for (int i = ndigits-1; i >= 0; i--) {
  1158.     buf[i] = "0123456789ABCDEF"[v&0xf];
  1159.     v >>= 4;
  1160.     }
  1161.     return (fxStr(buf, ndigits));
  1162. }
  1163.  
  1164. /* 
  1165.  * Hayes-style modem manipulation support.
  1166.  */
  1167. fxBool
  1168. FaxModem::reset(long ms)
  1169. {
  1170.     setDTR(FALSE);
  1171.     pause(conf.resetDelay);        // pause so modem can do reset
  1172.     setDTR(TRUE);
  1173.     /*
  1174.      * On some systems lowering and raising DTR is not done
  1175.      * properly (DTR is not raised when requested); thus we
  1176.      * reopen the device to insure that DTR is reasserted.
  1177.      */
  1178.     server.reopenDevice();
  1179.     if (!setBaudRate(rate, iFlow, oFlow))
  1180.     return (FALSE);
  1181.     flushModemInput();
  1182.     return atCmd(resetCmds, AT_OK, ms);
  1183. }
  1184.  
  1185. fxBool
  1186. FaxModem::abort(long ms)
  1187. {
  1188.     return reset(ms);
  1189. }
  1190.  
  1191. fxBool
  1192. FaxModem::sync(long ms)
  1193. {
  1194.     return waitFor(AT_OK, ms);
  1195. }
  1196.  
  1197. ATResponse
  1198. FaxModem::atResponse(char* buf, long ms)
  1199. {
  1200.     int n = getModemLine(buf, sizeof (rbuf), ms);
  1201.     if (n <= 0)
  1202.     lastResponse = (wasTimeout() ? AT_TIMEOUT : AT_EMPTYLINE);
  1203.     else if (strneq(buf, "OK", 2))
  1204.     lastResponse = AT_OK;
  1205.     else if (strneq(buf, "NO CARRIER", 10))
  1206.     lastResponse = AT_NOCARRIER;
  1207.     else if (strneq(buf, "NO DIAL", 7))        // NO DIALTONE or NO DIAL TONE
  1208.     lastResponse = AT_NODIALTONE;
  1209.     else if (strneq(buf, "NO ANSWER", 9))
  1210.     lastResponse = AT_NOANSWER;
  1211.     else if (strneq(buf, "ERROR", 5))
  1212.     lastResponse = AT_ERROR;
  1213.     else if (strneq(buf, "CONNECT", 7))
  1214.     lastResponse = AT_CONNECT;
  1215.     else if (strneq(buf, "RING", 4))
  1216.     lastResponse = AT_RING;
  1217.     else if (strneq(buf, "BUSY", 4))
  1218.     lastResponse = AT_BUSY;
  1219.     else if (strneq(buf, "PHONE OFF-HOOK", 14))
  1220.     lastResponse = AT_OFFHOOK;
  1221.     else
  1222.     lastResponse = AT_OTHER;
  1223.     return lastResponse;
  1224. }
  1225.  
  1226. /*
  1227.  * Send an AT command string to the modem and, optionally
  1228.  * wait for status responses.  This routine breaks multi-line
  1229.  * strings (demarcated by embedded \n's) and waits for each
  1230.  * intermediate response.  Embedded escape sequences for
  1231.  * changing the DCE-DTE communication rate and/or host-modem
  1232.  * flow control scheme are also recognized and handled.
  1233.  */
  1234. fxBool
  1235. FaxModem::atCmd(const fxStr& cmd, ATResponse r, long ms)
  1236. {
  1237.     u_int cmdlen = cmd.length();
  1238.     u_int pos = 0;
  1239.     fxBool respPending = FALSE;
  1240.  
  1241.     /*
  1242.      * Scan string for \n's and escape codes (byte w/ 0x80 set).
  1243.      * A \n causes the current string to be sent to the modem and
  1244.      * a return status string parsed (and possibly compared to an
  1245.      * expected response).  An escape code terminates scanning,
  1246.      * with any pending string flushed to the modem before the
  1247.      * associated commands are carried out.
  1248.      */
  1249.     u_int i = 0;
  1250.     while (i < cmdlen) {
  1251.     if (cmd[i] == '\n') {
  1252.         /*
  1253.          * Send partial string to modem and await
  1254.          * status string if necessary.
  1255.          */
  1256.         if (!putModemLine("AT" | cmd.extract(pos, i-pos)))
  1257.         return (FALSE);
  1258.         pos = ++i;            // next segment starts after \n
  1259.         if (r != AT_NOTHING) {
  1260.         if (!waitFor(r, ms))
  1261.             return (FALSE);
  1262.         } else {
  1263.         if (!waitFor(AT_OK, ms))
  1264.             return (FALSE);
  1265.         }
  1266.         respPending = FALSE;
  1267.     } else if (cmd[i] & 0x80) {
  1268.         /*
  1269.          * Escape code; flush any partial line, process
  1270.          * escape codes and carry out the associated
  1271.          * actions.  Note that assume there is no benefit
  1272.          * to set flow control independently of baud rate.
  1273.          */
  1274.         if (i > pos) {
  1275.         if (!putModemLine("AT" | cmd.extract(pos, i-pos)))
  1276.             return (FALSE);
  1277.         respPending = TRUE;
  1278.         }
  1279.         BaudRate br = rate;
  1280.         FlowControl flow = flowControl;
  1281.         do {
  1282.         switch (cmd[i] & 0xff) {
  1283.         case ESC_XON:    flow = FLOW_XONXOFF; break;
  1284.         case ESC_RTS:    flow = FLOW_RTSCTS; break;
  1285.         default:    br = cmd[i] & 0xf; break;
  1286.         }
  1287.         } while (++i < cmdlen && (cmd[i] & 0x80));
  1288.         pos = i;            // next segment starts here
  1289.         if (flow != flowControl)
  1290.         setBaudRate(br, flow, flow);
  1291.         else
  1292.         setBaudRate(br);
  1293.         rate = br; flowControl = flow;
  1294.     } else
  1295.         i++;
  1296.     }
  1297.     /*
  1298.      * Flush any pending string to modem.
  1299.      */
  1300.     if (i > pos) {
  1301.     if (!putModemLine("AT" | cmd.extract(pos, i-pos)))
  1302.         return (FALSE);
  1303.     respPending = TRUE;
  1304.     }
  1305.     /*
  1306.      * Wait for any pending response.
  1307.      */
  1308.     if (respPending) {
  1309.     if (r != AT_NOTHING && !waitFor(r, ms))
  1310.         return (FALSE);
  1311.     }
  1312.     return (TRUE);
  1313. }
  1314.  
  1315. /*
  1316.  * Wait (carefully) for some response from the modem.
  1317.  */
  1318. fxBool
  1319. FaxModem::waitFor(ATResponse wanted, long ms)
  1320. {
  1321.     for (;;) {
  1322.     ATResponse response = atResponse(rbuf, ms);
  1323.     if (response == wanted)
  1324.         return (TRUE);
  1325.     switch (response) {
  1326.     case AT_TIMEOUT:
  1327.     case AT_EMPTYLINE:
  1328.     case AT_ERROR:
  1329.     case AT_NOCARRIER:
  1330.     case AT_NODIALTONE:
  1331.     case AT_NOANSWER:
  1332.     case AT_OFFHOOK:
  1333.         modemTrace("ERROR: %s", ATresponses[response]);
  1334.         return (FALSE);
  1335.     }
  1336.     }
  1337. }
  1338.  
  1339. /*
  1340.  * Process a manufacturer/model/revision query.
  1341.  */
  1342. fxBool
  1343. FaxModem::doQuery(fxStr& queryCmd, fxStr& result, long ms)
  1344. {
  1345.     if (queryCmd == "")
  1346.     return (TRUE);
  1347.     if (queryCmd[0] == '!') {
  1348.     /*
  1349.      * ``!mumble'' is interpreted as "return mumble";
  1350.      * this means that you can't send ! to the modem.
  1351.      */
  1352.     result = queryCmd.tail(queryCmd.length()-1);
  1353.     return (TRUE);
  1354.     }
  1355.     return (atQuery(queryCmd, result, ms));
  1356. }
  1357.  
  1358. /*
  1359.  * Return modem manufacturer.
  1360.  */
  1361. fxBool
  1362. FaxModem::setupManufacturer(fxStr& mfr)
  1363. {
  1364.     return doQuery(mfrQueryCmd, mfr);
  1365. }
  1366.  
  1367. /*
  1368.  * Return modem model identification.
  1369.  */
  1370. fxBool
  1371. FaxModem::setupModel(fxStr& model)
  1372. {
  1373.     return doQuery(modelQueryCmd, model);
  1374. }
  1375.  
  1376. /*
  1377.  * Return modem firmware revision.
  1378.  */
  1379. fxBool
  1380. FaxModem::setupRevision(fxStr& rev)
  1381. {
  1382.     return doQuery(revQueryCmd, rev);
  1383. }
  1384.  
  1385. /*
  1386.  * Class X-style command support.
  1387.  */
  1388.  
  1389. /*
  1390.  * Send AT+F<cmd> and (potentially) wait for a response.
  1391.  */
  1392. fxBool
  1393. FaxModem::vatFaxCmd(ATResponse resp, const char* fmt ... )
  1394. {
  1395.     char buf[2048];
  1396.     buf[0] = '+'; buf[1] = 'F';
  1397.     va_list ap;
  1398.     va_start(ap, fmt);
  1399.     vsprintf(buf+2, fmt, ap);
  1400.     va_end(ap);
  1401.     return atCmd(buf, resp);
  1402. }
  1403.  
  1404. /*
  1405.  * Send AT<what>? and get a range response.
  1406.  */
  1407. fxBool
  1408. FaxModem::atQuery(const char* what, u_int& v, long ms)
  1409. {
  1410.     char response[1024];
  1411.     if (atCmd(what, AT_NOTHING) && atResponse(response) == AT_OTHER) {
  1412.     sync(ms);
  1413.     return parseRange(response, v);
  1414.     }
  1415.     return (FALSE);
  1416. }
  1417.  
  1418. /*
  1419.  * Send AT<what>? and get a string response.
  1420.  */
  1421. fxBool
  1422. FaxModem::atQuery(const char* what, fxStr& v, long ms)
  1423. {
  1424.     ATResponse r = AT_ERROR;
  1425.     if (atCmd(what, AT_NOTHING)) {
  1426.     v.resize(0);
  1427.     while ((r = atResponse(rbuf, ms)) != AT_OK) {
  1428.         if (r == AT_ERROR || r == AT_TIMEOUT || r == AT_EMPTYLINE)
  1429.         break;
  1430.         if (v.length())
  1431.         v.append('\n');
  1432.         v.append(rbuf);
  1433.     }
  1434.     }
  1435.     return (r == AT_OK);
  1436. }
  1437.  
  1438. const char OPAREN = '(';
  1439. const char CPAREN = ')';
  1440. const char COMMA = ',';
  1441. const char SPACE = ' ';
  1442.  
  1443. /*
  1444.  * Parse a Class 2 parameter range string.  This is very
  1445.  * forgiving because modem vendors do not exactly follow
  1446.  * the syntax specified in the "standard".  Try looking
  1447.  * at some of the responses given by rev ~4.04 of the
  1448.  * ZyXEL firmware (for example)!
  1449.  */
  1450. fxBool
  1451. FaxModem::vparseRange(const char* cp, int nargs ... )
  1452. {
  1453.     fxBool b = TRUE;
  1454.     va_list ap;
  1455.     va_start(ap, nargs);
  1456.     while (nargs-- > 0) {
  1457.     while (cp[0] == SPACE)
  1458.         cp++;
  1459.     char matchc;
  1460.     fxBool acceptList;
  1461.     if (cp[0] == OPAREN) {                // (<items>)
  1462.         matchc = CPAREN;
  1463.         acceptList = TRUE;
  1464.         cp++;
  1465.     } else if (isdigit(cp[0])) {            // <item>
  1466.         matchc = COMMA;
  1467.         acceptList = (nargs == 0);
  1468.     } else {
  1469.         b = FALSE;
  1470.         break;
  1471.     }
  1472.     int mask = 0;
  1473.     while (cp[0] && cp[0] != matchc) {
  1474.         if (cp[0] == SPACE) {            // ignore white space
  1475.         cp++;
  1476.         continue;
  1477.         }
  1478.         if (!isdigit(cp[0])) {
  1479.         b = FALSE;
  1480.         goto done;
  1481.         }
  1482.         int v = 0;
  1483.         do {
  1484.         v = v*10 + (cp[0] - '0');
  1485.         } while (isdigit((++cp)[0]));
  1486.         int r = v;
  1487.         if (cp[0] == '-') {                // <low>-<high>
  1488.         cp++;
  1489.         if (!isdigit(cp[0])) {
  1490.             b = FALSE;
  1491.             goto done;
  1492.         }
  1493.         r = 0;
  1494.         do {
  1495.             r = r*10 + (cp[0] - '0');
  1496.         } while (isdigit((++cp)[0]));
  1497.         } else if (cp[0] == '.') {            // <d.b>
  1498.         cp++;
  1499.         while (isdigit(cp[0]))            // XXX
  1500.             cp++;
  1501.         v++, r++;                // XXX 2.0 -> 3
  1502.         }
  1503.         // expand range or list
  1504.         for (; v <= r; v++)
  1505.         mask |= 1<<v;
  1506.         if (acceptList && cp[0] == COMMA)        // (<item>,<item>...)
  1507.         cp++;
  1508.     }
  1509.     *va_arg(ap, int*) = mask;
  1510.     if (cp[0] == matchc)
  1511.         cp++;
  1512.     if (matchc == CPAREN && cp[0] == COMMA)
  1513.         cp++;
  1514.     }
  1515. done:
  1516.     va_end(ap);
  1517.     return (b);
  1518. }
  1519.  
  1520. /*
  1521.  * Parse a single Class X range specification
  1522.  * and return the resulting bit mask.
  1523.  */
  1524. fxBool
  1525. FaxModem::parseRange(const char* cp, u_int& a0)
  1526. {
  1527.     return vparseRange(cp, 1, &a0);
  1528. }
  1529.  
  1530. void
  1531. FaxModem::setSpeakerVolume(SpeakerVolume l)
  1532. {
  1533.     atCmd(conf.setVolumeCmd[l]);
  1534. }
  1535.  
  1536. void
  1537. FaxModem::hangup()
  1538. {
  1539.     atCmd(conf.onHookCmd, AT_OK, 5000);
  1540. }
  1541.  
  1542. fxBool
  1543. FaxModem::waitForRings(u_int n)
  1544. {
  1545.     while (n > 0 && atResponse(rbuf, 10*1000) == AT_RING)
  1546.     n--;
  1547.     return (n <= 0);
  1548. }
  1549.